home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / JAVA_ALL / IDE / SUBARTIC / SUB_ARCT / LIB / HTML_ELE.JAV < prev    next >
Encoding:
Text File  |  1996-10-04  |  49.9 KB  |  1,710 lines

  1. package sub_arctic.lib;
  2.  
  3. import sub_arctic.input.*;
  4. import sub_arctic.output.*;
  5. import sub_arctic.lib.sub_arctic_error;
  6. import sub_arctic.constraints.std_function;
  7. import sub_arctic.constraints.std_constraint_consts;
  8.  
  9. import java.awt.Font;
  10. import java.awt.FontMetrics;
  11. import java.util.StringTokenizer;
  12. import java.util.Vector;
  13. import java.awt.Color;
  14.  
  15. /**
  16.  * This is a class for keeping around enough state process an
  17.  * HTML document.  It is probably not of interest to anyone 
  18.  * other than programmers implementing support for new HTML tags. 
  19.  * If you want to <I>use</I> HTML in your sub_arctic interfaces,
  20.  * look at the text_flow class. <P>
  21.  *
  22.  * This class keeps track of various display characteristics
  23.  * and the current contextual information. It also uses methods to  
  24.  * generate the various interactors that go on the display and these 
  25.  * may be overridden to provide support for new (and more complex)
  26.  * HTML tags. <P>
  27.  * 
  28.  * Basically this class keeps a static variable which is the 
  29.  * current "state" of the HTML parse. It is ostensibly a stack
  30.  * but because it is searched it various orders, it is implemented
  31.  * with a vector. You can use "push_element" and pop_element to
  32.  * pop elements of this class onto and off this stack.  Thus, 
  33.  * when a <B> tag is seen a new html_element is pushed
  34.  * onto the stack to note the font change; when the matching
  35.  * </B> tag is reached, the element is popped off the stack. <P>
  36.  * 
  37.  * The static methods calculate_font(), calculate_color(), and 
  38.  * calculate_indent() are used to compute the current font, color
  39.  * and indentation level whenever the stack is manipulated.
  40.  * Thus the variable _current_font is always the up-to-date
  41.  * value to use for creating new screen text.  If you put values
  42.  * in the public slots of the html_element for your specific
  43.  * subclass, these methods will pick those values up and add them
  44.  * to the current "context" for drawing operations. <P>
  45.  *
  46.  * The static method calculate_functions performs an <B>important</B>
  47.  * speed optimization that users of this code must understand.
  48.  * Since you want your new subclasses of html_element to work
  49.  * "in context" with other instances of other subclasses,
  50.  * you must "advertise" which functions of the API you implement.
  51.  * Once you have done so, calculate_functions will determine
  52.  * when to call your object and when to ignore it. This is
  53.  * a speed optimization which allows us to avoid walking the
  54.  * state stack every time we want to insert a word of text (or
  55.  * do any other parsing operation).  For example, the subclass
  56.  * numbered_element advertises that it implements the method
  57.  * list_item_prefix because all it cares about doing is inserting
  58.  * the correct prefix when a new LI tag is seen. A more complex
  59.  * example might be if you wanted some "context dependent" tag
  60.  * handling (such as supporting new tags which are only valid
  61.  * inside other tags) you would advertise that you implement
  62.  * the handle_tag operation.  <P>
  63.  * 
  64.  * The way you advertise what functions you implement is by 
  65.  * overriding the method functions_implemented and returning a set 
  66.  * of constants (bitwise ored together) from the static constants
  67.  * below.  <P>
  68.  * 
  69.  * The biggest weakness of this object right now is that it really
  70.  * can't handle anything that spans rows. Once we finish putting
  71.  * up a row, we throw everything about it away. This is probably
  72.  * going to make it hard in the future to do "align=RIGHT" type
  73.  * of stuff in IMG tags since to make it look nice it should span 
  74.  * multiple rows of text.<P>
  75.  *
  76.  */
  77. public class html_element implements std_constraint_consts {
  78.   /*****************************************************************/
  79.   /*                       STATIC CONSTANTS                        */
  80.   /*****************************************************************/
  81.   /* 
  82.    * These are the static constants that you should return
  83.    * from the method functions_implemented  
  84.    * (ored together) for the set of functions your object 
  85.    * implements
  86.    */
  87.   public static final int LIST_ITEM_PREFIX = 1;
  88.   public static final int HANDLE_TAG = 2;
  89.   public static final int ADD_SPACE = 4;
  90.   public static final int ADD_WORD = 8;
  91.   public static final int END_OF_LINE = 16;
  92.   public static final int ADD_LIST_ITEM = 32;
  93.   public static final int HANDLE_AMP = 64;
  94.   public static final int STRING_END = 128;
  95.   public static final int PARAGRAPH_END = 256;
  96.   public static final int PARAGRAPH_START = 512;
  97.  
  98.   /*****************************************************************/
  99.   /*                     STATIC STATE VARIABLES                    */
  100.   /*****************************************************************/
  101.   /**
  102.    * This is basically a data structure for keeping track of 
  103.    * the current state of the HTML parse so we can image correctly.
  104.    * We add html_elements to it and it gets walked to do things 
  105.    * like generate the current font and the current indentation level.
  106.    */
  107.   protected static Vector state;
  108.  
  109.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  110.  
  111.   /**
  112.    * This is where we store the current font as we are doing a 
  113.    * layout pass.
  114.    */
  115.   protected static Font _current_font;
  116.  
  117.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  118.  
  119.   /**
  120.    * This variable is the amount of indentation to use if a new line is
  121.    * called for.
  122.    */
  123.   protected static int _current_indent;
  124.  
  125.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  126.  
  127.   /**
  128.    * This is the row that we are currently constructing. 
  129.    */
  130.   protected static row _current_row;
  131.  
  132.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  133.  
  134.   /**
  135.    * This is the current set of colors in use.
  136.    */
  137.   protected static color_pair _current_colors;
  138.  
  139.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  140.  
  141.   /**
  142.    * This the text flow we are actually working on.
  143.    */
  144.   protected static text_flow _current_flow;
  145.  
  146.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  147.  
  148.   /**
  149.    * This is the current column (which is really the
  150.    * current paragraph) we are building. 
  151.    */
  152.   protected static column _current_column;
  153.  
  154.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  155.  
  156.   /** 
  157.    * This is a pointer to the html_element which is handling 
  158.    * LIST_ITEM_PREFIX.
  159.    */
  160.   protected static html_element _current_list_item_prefix;
  161.  
  162.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  163.  
  164.   /** 
  165.    * This is a pointer to the html_element which is handling 
  166.    * HANDLE_TAG
  167.    */
  168.   protected static html_element _current_handle_tag;
  169.  
  170.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  171.  
  172.   /** 
  173.    * This is a pointer to the html_element which is handling 
  174.    * ADD_SPACE
  175.    */
  176.   protected static html_element _current_add_space;
  177.  
  178.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  179.  
  180.   /** 
  181.    * This is a pointer to the html_element which is handling 
  182.    * ADD_WORD
  183.    */
  184.   protected static html_element _current_add_word;
  185.  
  186.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  187.  
  188.   /** 
  189.    * This is a pointer to the html_element which is handling 
  190.    * END_OF_LINE
  191.    */
  192.   protected static html_element _current_end_of_line;
  193.  
  194.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  195.  
  196.   /** 
  197.    * This is a pointer to the html_element which is handling 
  198.    * ADD_LIST_ITEM.
  199.    */
  200.   protected static html_element _current_add_list_item;
  201.  
  202.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  203.  
  204.   /** 
  205.    * This is a pointer to the html_element which is handling 
  206.    * HANDLE_AMP.
  207.    */
  208.   protected static html_element _current_handle_amp;
  209.  
  210.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  211.  
  212.   /** 
  213.    * This is a pointer to the html_element which is handling 
  214.    * STRING_END
  215.    */
  216.   protected static html_element _current_string_end;
  217.  
  218.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  219.  
  220.   /** 
  221.    * This is a pointer to the html_element which is handling 
  222.    * PARAGRAPH_END.
  223.    */
  224.   protected static html_element _current_paragraph_end;
  225.  
  226.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  227.  
  228.   /** 
  229.    * This is a pointer to the html_element which is handling 
  230.    * PARAGRAPH_START
  231.    */
  232.   protected static html_element _current_paragraph_start;
  233.  
  234.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  235.  
  236.   /**
  237.    * This is the string tokenizer we are using.
  238.    */
  239.   protected static StringTokenizer _tokenizer;
  240.  
  241.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  242.  
  243.   /**
  244.    * This is the amount of space between children.
  245.    */
  246.   protected static int _inter_child_space = 0;
  247.  
  248.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  249.  
  250.   /**
  251.    * This is where we hold the string we are currently
  252.    * building. This is used for the performance optimization
  253.    * so we don't have so many labels.
  254.    */
  255.   protected static StringBuffer _current_buffer;
  256.  
  257.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  258.  
  259.   /**
  260.    * This is where we keep track of how wide the string
  261.    * is that we have buffered in _current_buffer. This
  262.    * is to avoid having to consult the FontMetrics
  263.    * every time.
  264.    */
  265.   protected static int _current_buffer_width;
  266.  
  267.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  268.  
  269.   /**
  270.    * This is the basic font size that the layout algorithm
  271.    * is going to use
  272.    */
  273.   protected static int _basic_font_size=12;
  274.  
  275.   /*****************************************************************/
  276.   /*                 STATIC METHODS FOR SETTING VARS               */
  277.   /*****************************************************************/
  278.  
  279.   /**
  280.    * We need this for people who want to have switches for
  281.    * setting the size.  This will not take effect until the
  282.    * next parse of an HTML document.
  283.    *
  284.    * @param int s the new basic font size
  285.    */
  286.   public static void set_basic_font_size(int s) {
  287.     _basic_font_size=s;
  288.   }
  289.  
  290.   /*****************************************************************/
  291.   /*                 STATIC METHODS FOR PARSING HTML               */
  292.   /*****************************************************************/
  293.  
  294.   /**
  295.    * This function is called to initialize the parse and get the
  296.    * state stack set up for future things.
  297.    * 
  298.    * @param text_flow tf the text_flow we are laying out
  299.    * @param String text the text to put in the text_flow
  300.    */
  301.   protected static void init(text_flow tf,String text) {
  302.     
  303.     /* this guy is the root! */
  304.     html_element he=new html_element(true);
  305.  
  306.     /* set the state up */
  307.     state=new Vector(50);
  308.  
  309.     /* set up the tokenizer */
  310.     _tokenizer=new StringTokenizer(text," \t\n<",true);
  311.  
  312.     /* set the current text flow */
  313.     _current_flow=tf;
  314.  
  315.     /* get the string buffer read */
  316.     _current_buffer=new StringBuffer();
  317.  
  318.     /* set the current column to be a column with the same width as
  319.      * the current flow */
  320.     /* DANGER: IF YOU MODIFY THIS don't forget to modify
  321.        the one in paragraph_start() too */
  322.     _current_column=new column(0 /*ignored*/,0 /* ignored */,
  323.                    _current_flow.w()-(2*_current_flow.border()),
  324.                    10 /* ignored */, 
  325.                    0 /* no border because parent is doing it*/,
  326.                    _current_flow.interchild_space(),
  327.                    false,false,false /* crucial */,
  328.                    column.LEFT_JUSTIFIED,
  329.                    null);
  330.  
  331.     /* we want these guys to size their height by their children */
  332.     _current_column.set_h_constraint(std_function.offset(LAST_CHILD.Y2(), 0));
  333.  
  334.     /* this is something of a hack, but we have a dependency
  335.      * that is tough to get around. You'd like it to be the
  336.      * case that every time you change the state stack the
  337.      * current string buffered up is flushed into the
  338.      * display. However, we are trying to get the state stack
  339.      * initialized right now and thus _current_string_end 
  340.      * won't have a value yet. So, we are going to initialize
  341.      * it to a dead value, knowing it will get overwritten
  342.      * the first time the functions get calculated. trust me.*/
  343.     _current_string_end=new html_element();
  344.  
  345.     /* default font at font size in the text flow's font */
  346.     he.font_name=_current_flow.font_name();
  347.  
  348.     /* set the font size to be the one for this flow */
  349.     set_basic_font_size(_current_flow.font_size());
  350.     he.font_size=_basic_font_size;
  351.  
  352.     /* push it on the stack */
  353.     push_element(he);
  354.  
  355.     /* setup the first row */
  356.     /* WARNING: if you modify this don't forget to modify the one 
  357.      * in end_of_line */
  358.     _current_row=new row(0/*border */,_inter_child_space,
  359.              false,false,row.BOTTOM_JUSTIFIED);
  360.  
  361.     /* this setting of the size to zero is to insure that the row's
  362.        initial width (if any) could throw off the calculations of
  363.        the size of a row*/
  364.     _current_row.set_w(0);
  365.   }
  366.  
  367.    //had:
  368.    //* @exception general PROPAGATED
  369.  
  370.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  371.  
  372.   /**
  373.    * This function is called to handle the end of the HTML parse
  374.    * and general close down operations.
  375.    * 
  376.    */ 
  377.   protected static void finish() {
  378.  
  379.     /* clean up */
  380.     _current_end_of_line.end_of_line(0,false);
  381.  
  382.     /* finish this paragraph */
  383.     _current_paragraph_end.paragraph_end();
  384.  
  385.     /* we're done, so prevent things from working if we got into
  386.      this code again */
  387.     _current_row=null;
  388.     _current_column=null;
  389.   }
  390.  
  391.    //had:
  392.    //* @exception general PROPAGATED 
  393.   
  394.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  395.  
  396.   /**
  397.    * This function pushes things on the state vector. It also
  398.    * forces a recalculation of the current indentation level
  399.    * and current font.
  400.    * 
  401.    * @param html_element he the html element to push onto the state 
  402.    */
  403.   protected static void push_element(html_element he) {
  404.  
  405.     /* before we go changing the state, we need to tell the 
  406.        string handling code what's happening */
  407.     _current_string_end.string_end();
  408.     state.addElement(he);
  409.  
  410.     /* recalculate the state */
  411.     calculate_font();
  412.     calculate_indent();
  413.     calculate_colors();
  414.     calculate_functions();
  415.   }
  416.  
  417.    //had:
  418.    //* @exception bad_value PROPAGATED
  419.    //* @exception general PROPAGATED
  420.   
  421.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  422.  
  423.   /**
  424.    * This function removes things from the state vector. It
  425.    * also forces a recalculation of the current indentation
  426.    * level.
  427.    * 
  428.    * @return html_element although the element removed is returned, its not 
  429.    *                      clear what use that is.
  430.    */
  431.   protected static html_element pop_element() {
  432.     html_element el;
  433.  
  434.     if (state.isEmpty()) {
  435.       throw new sub_arctic_error("State stack empty! Probably caused by " +
  436.               "badly formed HTML");
  437.     }
  438.  
  439.     /* before we go changing the state, we need to tell the 
  440.        string handling code what's happening */
  441.     _current_string_end.string_end();
  442.     el=(html_element)state.elementAt(state.size()-1);
  443.     state.removeElementAt(state.size()-1);
  444.  
  445.     /* recalculate the state */
  446.     calculate_font();
  447.     calculate_indent();
  448.     calculate_colors();
  449.     calculate_functions();
  450.     /* return the element */
  451.     return el;
  452.   }
  453.  
  454.    //had:
  455.    //* @exception bad_value is thrown if the stack is empty when you try to pop it
  456.    //* @exception general PROPAGATED
  457.  
  458.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  459.  
  460.   /**
  461.    * This function calculates the current font by walking the 
  462.    * state vector. It's policy is that it uses the most current
  463.    * font and the most current font size. It does a binary or of 
  464.    * all font modifiers found. Note that the the state is walked
  465.    * from oldest element to newest element.
  466.    * 
  467.    */
  468.   protected static void calculate_font() {
  469.     String name=null;
  470.     int size=0,mods=0,i;
  471.     html_element he;
  472.  
  473.     /* validity check */
  474.     if (state.size()==0) {
  475.       throw new sub_arctic_error("States stack is empty!");
  476.     }
  477.  
  478.     /* walk the state */
  479.     for (i=0; i<state.size(); ++i) {
  480.       he=(html_element)state.elementAt(i);
  481.  
  482.       /* copy the values */
  483.       if (he.font_name!=null) {
  484.     name=he.font_name;
  485.       }
  486.       if (he.font_size!=0) {
  487.     size=he.font_size;
  488.       }
  489.  
  490.       /* always or in this value... it can't hurt! */
  491.       mods|=he.font_modifier;
  492.     }
  493.  
  494.     /* validity check the result */
  495.     if ((name==null) ||
  496.     (size==0)) {
  497.       throw new sub_arctic_error("The state stack didn't produce a valid font");
  498.     }
  499.  
  500.     /* set the font */
  501.     _current_font=new Font(name,mods,size);
  502.   }
  503.  
  504.    //had:
  505.    //* @exception bad_value will be thrown if insufficient font information  
  506.    //*                      is found to generate a font.
  507.  
  508.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  509.  
  510.   /**
  511.    * This function computes the current indent level by walking
  512.    * down the state and adding up the current amount of indentation.
  513.    */
  514.   protected static void calculate_indent() {
  515.     int i,indent=0;
  516.     html_element he;
  517.  
  518.     /* validity check */
  519.     if (state.size()==0) {
  520.       throw new sub_arctic_error("States stack is empty!");
  521.     }
  522.  
  523.     /* walk the state */
  524.     for (i=0; i<state.size(); ++i) {
  525.       he=(html_element)state.elementAt(i);
  526.       /* ok add this one in */
  527.       indent+=he.indent_contribution;
  528.     }
  529.  
  530.     /* totaled it up, stuff it in */
  531.     _current_indent=indent;
  532.   }
  533.  
  534.    //had:
  535.    //* @exception bad_value is thrown if the state stack is empty
  536.  
  537.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  538.  
  539.   /**
  540.    * This function calculates the current color to be drawing
  541.    * in. If it doesn't find anything, it uses the default system
  542.    * colors.
  543.    */
  544.   public static void calculate_colors() {
  545.     int i;
  546.     html_element he;
  547.     color_pair cp=null;
  548.  
  549.     /* walk the state */
  550.     for (i=state.size()-1; i>=0; --i) {
  551.       he=(html_element)state.elementAt(i);
  552.       if (he.colors!=null) {
  553.     cp=he.colors;
  554.     break;
  555.       }
  556.     }
  557.  
  558.     /* figure it out ... if its null then we use the system defaults */
  559.     if (cp==null) {
  560.       _current_colors=manager.default_color_pair();
  561.     } else {
  562.       _current_colors=cp;
  563.     }
  564.   }
  565.  
  566.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  567.  
  568.   /**
  569.    * This function calculates the current set of objects which are
  570.    * handling parsing operations. This basically walks backwards 
  571.    * through the state stack looking at what functions each
  572.    * object is advertising that it implements. If one is found,
  573.    * it is put in the slot for whatever functions it implements.
  574.    * This process proceeds until the root is encountered or
  575.    * state stack is emptied.
  576.    * 
  577.    */
  578.   public static void calculate_functions() {
  579.     int i;
  580.     int implemented;
  581.     html_element el;
  582.  
  583.     /* we start out looking for everything */
  584.     int mask=LIST_ITEM_PREFIX | HANDLE_TAG |  ADD_SPACE |  END_OF_LINE |
  585.       ADD_LIST_ITEM |  ADD_WORD | HANDLE_AMP | STRING_END | PARAGRAPH_END |
  586.       PARAGRAPH_START;
  587.     
  588.     /* work our way backwards */
  589.     for (i=state.size()-1; i>=0; --i) {
  590.       el=(html_element)state.elementAt(i);
  591.  
  592.       /* retrieve the ones they are using */
  593.       implemented=el.functions_implemented();
  594.  
  595.       /* test for function */
  596.       if (((mask & LIST_ITEM_PREFIX)!=0) && 
  597.       ((implemented & LIST_ITEM_PREFIX)!=0)) {
  598.     _current_list_item_prefix=el;
  599.     /* we don't need it anymore */
  600.     mask ^= LIST_ITEM_PREFIX;
  601.       }
  602.  
  603.       /* test for function */
  604.       if (((mask & HANDLE_TAG)!=0) && 
  605.       ((implemented & HANDLE_TAG)!=0)) {
  606.     _current_handle_tag=el;
  607.     /* we don't need it anymore */
  608.     mask ^= HANDLE_TAG;
  609.       }
  610.  
  611.       /* test for function */
  612.       if (((mask & ADD_SPACE)!=0) && 
  613.       ((implemented & ADD_SPACE)!=0)) {
  614.     _current_add_space=el;
  615.     /* we don't need it anymore */
  616.     mask ^= ADD_SPACE;
  617.       }
  618.  
  619.       /* test for function */
  620.       if (((mask & ADD_WORD)!=0) && 
  621.       ((implemented & ADD_WORD)!=0)) {
  622.     _current_add_word=el;
  623.     /* we don't need it anymore */
  624.     mask ^= ADD_WORD;
  625.       }
  626.  
  627.       /* test for function */
  628.       if (((mask & END_OF_LINE)!=0) && 
  629.       ((implemented & END_OF_LINE)!=0)) {
  630.     _current_end_of_line=el;
  631.     /* we don't need it anymore */
  632.     mask ^= END_OF_LINE;
  633.       }
  634.  
  635.       /* test for function */
  636.       if (((mask & ADD_LIST_ITEM)!=0) && 
  637.       ((implemented & ADD_LIST_ITEM)!=0)) {
  638.     _current_add_list_item=el;
  639.     /* we don't need it anymore */
  640.     mask ^= ADD_LIST_ITEM;
  641.       }
  642.  
  643.       /* test for function */
  644.       if (((mask & HANDLE_AMP)!=0) && 
  645.       ((implemented & HANDLE_AMP)!=0)) {
  646.     _current_handle_amp=el;
  647.     /* we don't need it anymore */
  648.     mask ^= HANDLE_AMP;
  649.       }
  650.  
  651.       /* test for function */
  652.       if (((mask & STRING_END)!=0) && 
  653.       ((implemented & STRING_END)!=0)) {
  654.     _current_string_end=el;
  655.     /* we don't need it anymore */
  656.     mask ^= STRING_END;
  657.       }
  658.  
  659.       /* test for function */
  660.       if (((mask & PARAGRAPH_END)!=0) && 
  661.       ((implemented & PARAGRAPH_END)!=0)) {
  662.     _current_paragraph_end=el;
  663.     /* we don't need it anymore */
  664.     mask ^= PARAGRAPH_END;
  665.       }
  666.  
  667.       /* test for function */
  668.       if (((mask & PARAGRAPH_START)!=0) && 
  669.       ((implemented & PARAGRAPH_START)!=0)) {
  670.     _current_paragraph_start=el;
  671.     /* we don't need it anymore */
  672.     mask ^= PARAGRAPH_START;
  673.       }
  674.  
  675.       /* if we have all the functions, we can bail out */
  676.       if (mask==0) break;
  677.     }
  678.  
  679.     /* we are ostensibly done, we should check to make sure everything
  680.      * worked */
  681.     if (mask!=0) {
  682.       throw new sub_arctic_error("Not all parsing functions implemented");
  683.     }
  684.   }
  685.  
  686.    //had:
  687.    //* @exception bad_value gets thrown if it can't find any object on the 
  688.    //*                      state stack willing to handle one of the functions.
  689.  
  690.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  691.  
  692.   /**
  693.    * This function is useful for "backchaining" in the state
  694.    * stack to an "older" version of a function. E.g. you are
  695.    * implementing some new context-dependent tags and you find
  696.    * a tag you are not interested in, just call this function
  697.    * with the HANDLE_TAG mask and yourself as the object to
  698.    * look behind and you'll get returned an object on which 
  699.    * you can call the handle_tag method and hand it the tag
  700.    * it should process.<p>
  701.    * 
  702.    * This function might also be useful for finding out who
  703.    * is "behind" you in the context. E.g. if you wanted to
  704.    * make the numbering tags do dots between subordinate
  705.    * lists. E.g. the second level list would 2.1, 2.2,
  706.    * 2.3, etc.
  707.    * 
  708.    * @param int          fn   the function code to find (must be one of the 
  709.    *                          static constants above.
  710.    * @param html_element elem the object to start looking "behind" (note that 
  711.    *                          this function will return null if the element is 
  712.    *                          the root).
  713.    * @return html_element element implementing the function in question.
  714.    */
  715.  
  716.   public static html_element previous_function(int fn, html_element elem) {
  717.     int index=state.indexOf(elem),i;
  718.     html_element el;
  719.  
  720.     /* bad element?        */
  721.     if (index==-1) {
  722.       throw new sub_arctic_error("Element is not in the state stack");
  723.     }
  724.  
  725.     /* the root? */
  726.     if (index==0) {
  727.       return null;
  728.     }
  729.  
  730.     /* normal case */
  731.     for (i=index-1; i>=0; --i) {
  732.       el=(html_element)state.elementAt(i);
  733.       if ((el.functions_implemented() & fn)!=0) {
  734.     return el;
  735.       }
  736.     }
  737.  
  738.     /* didn't find it, something's wrong */
  739.     throw new sub_arctic_error("Unable to locate the function " + fn + 
  740.             " in any object in the state stack");
  741.   }
  742.  
  743.    //had:
  744.    //*@exception bad_value is thrown if no matching object is found (this 
  745.    //*                     shouldn't happen if the root is set up right) or you 
  746.    //*                     pass a bad function constant or if the object passed 
  747.    //*                     as the element is not in the state stack.
  748.  
  749.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  750.  
  751.   /**
  752.    * This is the main driver function for this parsing operation.
  753.    */
  754.   public static void parse() {
  755.     String tok;
  756.     
  757.     /* loop over all the tokens */
  758.     while (_tokenizer.hasMoreTokens()) {
  759.  
  760.       /* look for a normal token */
  761.       tok=_tokenizer.nextToken(" \t\n<&");
  762.  
  763.       /* is it the beginning of a tag ? */
  764.       if (tok.indexOf("<")!=-1) {
  765.  
  766.     /* fish out the tag by changing the delimiter */
  767.     tok=_tokenizer.nextToken(">");
  768.  
  769.     /* tell the tag handling process about it */
  770.     _current_handle_tag.handle_tag(tok);
  771.  
  772.     /* we know there is a greater than pending so lets get 
  773.        rid of it */
  774.     _tokenizer.nextToken(">");
  775.     continue;
  776.       }
  777.  
  778.       /* if it starts with an ampersand its a special */
  779.       if (tok.startsWith("&")) {
  780.     tok=_tokenizer.nextToken(";");
  781.     _current_handle_amp.handle_amp(tok);
  782.  
  783.     /* get rid of the extra ; */
  784.     _tokenizer.nextToken(";"); 
  785.     continue;
  786.       }
  787.  
  788.       /* it doesn't have a less than  in it, so let's try looking
  789.      for space, newline and or tab */
  790.       if ((tok.indexOf(" ")!=-1) ||
  791.       (tok.indexOf("\t")!=-1) ||
  792.       (tok.indexOf("\n")!=-1)) {
  793.     /* its some sort of whitespace, tell us about it */
  794.     _current_add_space.add_space();
  795.     continue;
  796.       }
  797.  
  798.       /* ok, we now know its a plain old word so let's just add it */
  799.       _current_add_word.add_word(tok);
  800.     }
  801.  
  802.     /* we are done with it */
  803.     finish();
  804.   }
  805.  
  806. /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  807.  
  808.   /**
  809.    * This function determines if a row is empty or not. 
  810.    * @param row r the row to check
  811.    * @return boolean true if the row is empty
  812.    */
  813.   public static boolean row_is_empty(row r) {
  814.     if ((r.num_children()==0) ||
  815.     ((r.num_children()==1) &&
  816.      (r.child(0) instanceof spacer))) {
  817.       return true;
  818.     }
  819.     return false;
  820.   }
  821.  
  822.    //had:
  823.    //* @exception general PROPAGATED
  824.  
  825.   /*****************************************************************/
  826.   /*                         INSTANCE VARIABLES                    */
  827.   /*****************************************************************/
  828.   /**
  829.    * This variable tells us if we are the root of the state stack
  830.    * or not. If this is true we will export all the functions in
  831.    * the API to their default values. 
  832.    */
  833.   private boolean root;
  834.  
  835.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  836.  
  837.   /** use this for tags that actually change the base font */
  838.   /* if this is NULL it is ignored */
  839.   public String font_name;
  840.  
  841.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  842.  
  843.   /** use this for tags that SET the size of the font */
  844.   /* if this is 0 it gets ignored */
  845.   public int font_size;
  846.  
  847.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  848.  
  849.   /** this is a font modifier for things like bold and italic
  850.    * it gets ORed into the font modifier mask ... since plain is
  851.    * zero you can just let plain be the value if you don't want 
  852.    * anything snazzy */
  853.   public int font_modifier;
  854.  
  855.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  856.  
  857.   /** this is the amount of indentation that is tag's contribution
  858.    * to future rows. Its not an absolute amount but a contribution */
  859.   public int indent_contribution;
  860.  
  861.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  862.  
  863.   /**
  864.    * This is the color_pair to use for drawing the text. If you
  865.    * don't do anything, you'll get null and the system will use
  866.    * the default system colors.
  867.    */
  868.   public color_pair colors;
  869.  
  870.   /*****************************************************************/
  871.   /*                   OVERRIDABLE METHODS                         */
  872.   /*****************************************************************/
  873.  
  874.   /**
  875.    * Construct a html_element. This constructor makes all the fields
  876.    * have ignored values and this object is "dead" with respect 
  877.    * to what functions it implements.
  878.    */
  879.   public html_element() {
  880.     root=false;
  881.     font_name=null;
  882.     font_size=0;
  883.     font_modifier=0;
  884.     indent_contribution=0;
  885.     colors=null;
  886.   }
  887.  
  888.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  889.  
  890.   /**
  891.    * Construct an HTML element which is the root of the state
  892.    * stack by passing true here. You should ONLY do this if you
  893.    * intend that all objects below this one in the state stack
  894.    * have their functions ignored (which is only likely to be
  895.    * useful if you are putting this at the bottom of the 
  896.    * state stack.  This still makes all the fields have 
  897.    * their default (ignored) values.
  898.    *
  899.    * @param boolean b true if this object is a root object.
  900.    *
  901.    */
  902.   public html_element(boolean b) {
  903.     root=b;
  904.     font_name=null;
  905.     font_size=0;
  906.     font_modifier=0;
  907.     indent_contribution=0;
  908.     colors=null;
  909.   }
  910.  
  911.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  912.  
  913.   /**
  914.    * Override this method to inform the system what parsing
  915.    * methods you handle.
  916.    */
  917.   public int functions_implemented() {
  918.     if (root) {
  919.       /* if we are the root, we implement everything */
  920.       return (LIST_ITEM_PREFIX | HANDLE_TAG |  ADD_SPACE |  END_OF_LINE |
  921.           ADD_LIST_ITEM |  ADD_WORD | HANDLE_AMP | STRING_END |
  922.           PARAGRAPH_END | PARAGRAPH_START);
  923.     } else {
  924.       /* if we aren't a root, we are a pass thru */
  925.       return 0;
  926.     }
  927.   }
  928.  
  929.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  930.  
  931.   /**
  932.    * This function is called to generate a prefix interactor for 
  933.    * list elements. It should return an interactor to put right in
  934.    * front of the actual item. Thus subclasses like the 
  935.    * numbered_element return a label with the correct number
  936.    * in it for this function...
  937.    * 
  938.    */
  939.   public interactor list_item_prefix() 
  940.   {
  941.     return null;
  942.   }
  943.  
  944.    //had:
  945.    //* @exception general may be thrown by subclasses
  946.  
  947.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  948.  
  949.   /**
  950.    * This function gets called to implement a tags behavior. Since
  951.    * Java can't handle pointers to functions, you'll just have to
  952.    * check for each string and then add your code at the appropriate
  953.    * place.  
  954.    */
  955.   protected void handle_tag(String tag) 
  956.   {
  957.     /*
  958.      * Get the <P> tag
  959.      */
  960.     if (tag.equalsIgnoreCase("P")) {
  961.       spacer sp=new spacer(1,manager.get_metrics(_current_font).getAscent());
  962.       /* finish this line */
  963.       _current_end_of_line.end_of_line(0,false);
  964.       /* finish this paragraph */
  965.       _current_paragraph_end.paragraph_end();
  966.       /* start a new paragraph */
  967.       _current_paragraph_start.paragraph_start();
  968.       /* put a spacer in that is the height of a T */
  969.       _current_row.add_child(sp);
  970.       /* finish that line */
  971.       _current_end_of_line.end_of_line(0,true);
  972.       /* finish blank paragraph */
  973.       _current_paragraph_end.paragraph_end();
  974.       /* start a new paragraph */
  975.       _current_paragraph_start.paragraph_start();
  976.       /* done */
  977.       return;
  978.     }
  979.  
  980.     /*
  981.      * We are going to intentionally ignore the /P tag so things 
  982.      * will work ok if people form their HTML "correctly." 
  983.      */
  984.     if (tag.equalsIgnoreCase("/P")) {
  985.       return;
  986.     }
  987.  
  988.     /*
  989.      *  Get the <BR> tag.
  990.      */
  991.     if (tag.equalsIgnoreCase("BR")) {
  992.       /* line is done */
  993.       _current_end_of_line.end_of_line(0,false);
  994.       return;
  995.     }
  996.  
  997.     /*
  998.      * I don't know why you would use the /BR tag but just in case...
  999.      */
  1000.     if (tag.equalsIgnoreCase("/BR")) {
  1001.       return;
  1002.     }
  1003.  
  1004.     /**
  1005.      * The <CENTER> tag
  1006.      */
  1007.     if (tag.equalsIgnoreCase("CENTER")) {
  1008.  
  1009.       /* implies end of line */
  1010.       _current_end_of_line.end_of_line(0,false);
  1011.  
  1012.       /* end of paragraph */
  1013.       _current_paragraph_end.paragraph_end();
  1014.  
  1015.       /* push us the stack */
  1016.       push_element(new centered_element());
  1017.  
  1018.       /* now start a new paragraph */
  1019.       _current_paragraph_start.paragraph_start();
  1020.     }
  1021.  
  1022.     /* pop off the center object from the state stack */
  1023.     if (tag.equalsIgnoreCase("/CENTER")) {
  1024.  
  1025.       /* end the line */
  1026.       _current_end_of_line.end_of_line(0,false);
  1027.  
  1028.       /* end of paragraph */
  1029.       _current_paragraph_end.paragraph_end();
  1030.  
  1031.       /* remove us from the state stack */
  1032.       pop_element();
  1033.  
  1034.       /* start over */
  1035.       _current_paragraph_start.paragraph_start();
  1036.     }
  1037.  
  1038.     /*
  1039.      * The <LI> tag is one that needs to be deal with specially.
  1040.      */
  1041.     if (tag.equals("LI")) {
  1042.       _current_add_list_item.add_list_item();
  1043.       return;
  1044.     }
  1045.  
  1046.     /*
  1047.      * <B> tag
  1048.      */
  1049.     if (tag.equalsIgnoreCase("B")) {
  1050.       /* create a new element with bold in it and push it */
  1051.       html_element he=new html_element();
  1052.       he.font_modifier=Font.BOLD;
  1053.       push_element(he);
  1054.     }
  1055.  
  1056.     /*
  1057.      * </B>
  1058.      */
  1059.     if (tag.equalsIgnoreCase("/B")) {
  1060.       pop_element();
  1061.     }
  1062.  
  1063.     /*
  1064.      * <I> tag
  1065.      */
  1066.     if (tag.equalsIgnoreCase("I")) {
  1067.       /* create a new element with bold in it and push it */
  1068.       html_element he=new html_element();
  1069.       he.font_modifier=Font.ITALIC;
  1070.       push_element(he);
  1071.     }
  1072.  
  1073.     /*
  1074.      * </I>
  1075.      */
  1076.     if (tag.equalsIgnoreCase("/I")) {
  1077.       pop_element();
  1078.     }
  1079.  
  1080.     /**
  1081.      * <OL> tag
  1082.      */
  1083.     if (tag.equalsIgnoreCase("OL")) {
  1084.       html_element he = new numbered_element();
  1085.       push_element(he);
  1086.     }
  1087.  
  1088.     /*
  1089.      * </OL>
  1090.      */
  1091.     if (tag.equalsIgnoreCase("/OL")) {
  1092.       pop_element();
  1093.     }
  1094.  
  1095.     /**
  1096.      * <UL> tag
  1097.      */
  1098.     if (tag.equalsIgnoreCase("UL")) {
  1099.       html_element he = new non_numbered_element();
  1100.       push_element(he);
  1101.     }
  1102.  
  1103.     /*
  1104.      * </UL>
  1105.      */
  1106.     if (tag.equalsIgnoreCase("/UL")) {
  1107.       pop_element();
  1108.     }
  1109.  
  1110.     /*
  1111.      * H1 is really big (24 pt)
  1112.      */
  1113.     if (tag.equalsIgnoreCase("H1")) { 
  1114.       html_element he=new html_element();
  1115.       he.font_size=_basic_font_size+12;
  1116.       he.font_modifier=Font.BOLD;
  1117.       push_element(he);
  1118.     }
  1119.  
  1120.     /*
  1121.      * H2 is pretty big (16 pt)
  1122.      */
  1123.     if (tag.equalsIgnoreCase("H2")) { 
  1124.       html_element he=new html_element();
  1125.       he.font_size=_basic_font_size+4;
  1126.       he.font_modifier=Font.BOLD;
  1127.       push_element(he);
  1128.     }
  1129.  
  1130.     /*
  1131.      * H3 is big (14 pt)
  1132.      */
  1133.     if (tag.equalsIgnoreCase("H3")) { 
  1134.       html_element he=new html_element();
  1135.       he.font_size=_basic_font_size+2;
  1136.       he.font_modifier=Font.BOLD;
  1137.       push_element(he);
  1138.     }
  1139.  
  1140.     /*
  1141.      * H4 is normal sized (12 pt)
  1142.      */
  1143.     if (tag.equalsIgnoreCase("H4")) { 
  1144.       html_element he=new html_element();
  1145.       he.font_size=_basic_font_size;
  1146.       he.font_modifier=Font.BOLD;
  1147.       push_element(he);
  1148.     }
  1149.  
  1150.     /**
  1151.      * H5 is smaller than normal (10 pt)
  1152.      */
  1153.     if (tag.equalsIgnoreCase("H5")) {
  1154.       html_element he=new html_element();
  1155.       he.font_size=_basic_font_size-2;
  1156.       he.font_modifier=Font.BOLD;
  1157.       push_element(he);
  1158.     }
  1159.  
  1160.     /**
  1161.      * H6 is much smaller than normal (8 pt)
  1162.      */
  1163.     if (tag.equalsIgnoreCase("H6")) {
  1164.       html_element he=new html_element();
  1165.       he.font_size=_basic_font_size-4;
  1166.       he.font_modifier=Font.BOLD;
  1167.       push_element(he);
  1168.     }
  1169.  
  1170.     /**
  1171.      * Any tag staring with /H we do the same thing
  1172.      */
  1173.     if (tag.toUpperCase().startsWith("/H")) {
  1174.       pop_element();
  1175.       _current_end_of_line.end_of_line(0,false);
  1176.     }
  1177.  
  1178.     /**
  1179.      * KBD (keyboard) is one that I use a lot ... You get a courier
  1180.      * font for that "computerish" look.
  1181.      */
  1182.     if (tag.equalsIgnoreCase("KBD")) {
  1183.       html_element he=new html_element();
  1184.       he.font_name="Courier";
  1185.       push_element(he);
  1186.     }
  1187.  
  1188.     /* 
  1189.      * /KBD
  1190.      */
  1191.     if (tag.equalsIgnoreCase("/KBD")) {
  1192.       pop_element();
  1193.     }
  1194.  
  1195.   }
  1196.  
  1197.    //had:
  1198.    //* @exception general PROPAGATED 
  1199.  
  1200.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1201.  
  1202.   /**
  1203.    * This function gets called to add whitespace to the display.
  1204.    */
  1205.   protected void add_space() 
  1206.   {
  1207.     int w;
  1208.  
  1209.     /* if we are at the left edge of a row, don't bother */
  1210.     if (row_is_empty(_current_row) && (_current_buffer.length()==0)) {
  1211.       return;
  1212.     }
  1213.  
  1214.     /* ask the manager how wide a space is */
  1215.     w=manager.get_metrics(_current_font).stringWidth(" ");
  1216.  
  1217.     /* are we going to force a new string? */
  1218.     if (w+_current_buffer_width >= _current_column.w()) {
  1219.       /* don't bother, we'll just end this line */
  1220.       _current_end_of_line.end_of_line(0,false);
  1221.       return;
  1222.     }
  1223.  
  1224.     /* check to see if there is a string buffer contents now */
  1225.     if (_current_buffer.length()!=0) {
  1226.       /* the buffer has a string in it, so just add a space char */
  1227.       _current_buffer.append(" ");
  1228.       /* update how much space the string occupies */
  1229.       _current_buffer_width+=w;
  1230.       return;
  1231.     }
  1232.  
  1233.     /* nothing buffered, use a spacer */
  1234.     spacer sp=new spacer(w,1);
  1235.  
  1236.     /* put the space in there */
  1237.     _current_row.add_child(sp); 
  1238.   }
  1239.  
  1240.    //had:
  1241.    //* @exception general PROPAGATED
  1242.  
  1243.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1244.  
  1245.   /**
  1246.    * This function does a calculation to see if the new word will fit 
  1247.    * on this line. If it does, it just adds it to the current row. 
  1248.    * If not, it creates a new line and adds the word to that line, 
  1249.    * without considering if it fits. (Note: This means that words 
  1250.    * that are wider than the width of the parent just get clipped.)
  1251.    * 
  1252.    * @param String word the text of the word to add
  1253.    */
  1254.   protected void add_word(String word) 
  1255.   {
  1256.     int parent_width=_current_column.w(), row_width=_current_row.w();
  1257.     int string_width;
  1258.     FontMetrics fm=manager.get_metrics(_current_font);
  1259.  
  1260.     /* this is the string we are adding to the display plus 
  1261.        any buffered text's width */
  1262.     string_width=fm.stringWidth(word) + _current_buffer_width;
  1263.  
  1264.     /* case 1: row is empty or has only a spacer on it: we always
  1265.      * put this word on such a line */
  1266.     if (row_is_empty(_current_row) && (_current_buffer.length()==0)) {
  1267.  
  1268.       /* we always add it because otherwise you'll get a blank line
  1269.      if this object is larger than the overall flow */
  1270.       _current_buffer.append(word);
  1271.  
  1272.       /* set the buffer width appropriately */
  1273.       _current_buffer_width=string_width;
  1274.       return;
  1275.     }
  1276.  
  1277.     /* is it too big? */
  1278.     if (string_width + row_width + _inter_child_space > parent_width) {
  1279.  
  1280.       /* case 2: too big ... finish this line and make a new one*/
  1281.       _current_end_of_line.end_of_line(0,false);
  1282.  
  1283.       /* put the string on the next line */
  1284.       _current_buffer.append(word);
  1285.  
  1286.       /* store the amount of text we have left */
  1287.       _current_buffer_width=fm.stringWidth(word);
  1288.  
  1289.     } else {
  1290.  
  1291.       /* case 3: this is the normal case */
  1292.       _current_buffer.append(word);
  1293.  
  1294.       /* remember how much we have buffered */
  1295.       _current_buffer_width=string_width;
  1296.     }
  1297.   }
  1298.  
  1299.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1300.  
  1301.   /**
  1302.    * This function is called to inform us that the current string
  1303.    * needs to be inserted into the row. This allows us (usually)
  1304.    * to make one big label for the string, rather than having 
  1305.    * a string for each word (which really slows things down).  
  1306.    * If you are implementing tags which will insert things into 
  1307.    * the current row, you'll probably need to call this function
  1308.    * to make sure all the text is there. 
  1309.    * 
  1310.    */
  1311.   public void string_end() 
  1312.   {
  1313.     label l;
  1314.  
  1315.     /* empty strings means no work */
  1316.     if (_current_buffer.length()==0) return;
  1317.  
  1318.     /* not empty so make a label */
  1319.     l=new label(_current_buffer.toString(),_current_font);
  1320.  
  1321.     /* fix the spacing */
  1322.     l.set_above_spacing(0);
  1323.  
  1324.     /* XXX should be figuring out the descent size ... it appears that
  1325.        the descent size in many cases is WAY larger than it needs to be 
  1326.        so just use 3 instead XX */
  1327.     /* l.set_below_spacing(3); */
  1328.     l.set_below_spacing(manager.get_metrics(_current_font).getDescent());
  1329.     l.set_h_spacing(0);
  1330.  
  1331.     /* set the colors */
  1332.     l.set_draw_colors(_current_colors);
  1333.     _current_row.add_child(l);
  1334.  
  1335.     /* reset the buffer */
  1336.     _current_buffer=new StringBuffer();
  1337.  
  1338.     /* reset the buffer's size */
  1339.     _current_buffer_width=0;
  1340.   }
  1341.  
  1342.    //had:
  1343.    //* @exception general PROPAGATED
  1344.  
  1345.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1346.  
  1347.   /**
  1348.    * This function gets called to polish off the current line.
  1349.    * It takes the current row and adds to the current column 
  1350.    * and then sets up a new row. If you supply an
  1351.    * argument, that much is subtracted from the normal indentation
  1352.    * for this row. This is highly useful for keeping things aligned
  1353.    * in the presence of list items marking the paragraphs.
  1354.    * 
  1355.    * @param int     shift the amount you want subtracted from the indentation.
  1356.    * @param boolean hard  true if you are sure you want to <I>force</I> the 
  1357.    *                      end of a line, even if you are at the beginning of 
  1358.    *                      an empty row. Use false to indicate that you just 
  1359.    *                      want to start at the beginning of a line.
  1360.    */
  1361.   public void end_of_line(int shift, boolean hard) 
  1362.   {
  1363.     /* deal with soft end of line */
  1364.     if (hard==false) {
  1365.       /* is this an empty row? */
  1366.       if (row_is_empty(_current_row) && (_current_buffer.length()==0)) { 
  1367.     /* its empty and they wanted a soft eol */
  1368.     return;
  1369.       }
  1370.     }
  1371.  
  1372.     /* make sure we put in the buffered text */
  1373.     _current_string_end.string_end();
  1374.  
  1375.     /* put the row we just finished into the column (us) */
  1376.     _current_column.add_child(_current_row);
  1377.  
  1378.     /* WARNING: if you modify this don't forget to modify the one 
  1379.      * in init(...) */
  1380.     _current_row=new row(0,_inter_child_space,
  1381.              false,false,row.BOTTOM_JUSTIFIED);
  1382.  
  1383.     /* this setting of the size to zero is to insure that the row's
  1384.        initial width (if any) could throw off the calculations of
  1385.        the size of a row*/
  1386.     _current_row.set_w(0);
  1387.  
  1388.     /* if we don't need any space, don't bother */
  1389.     if (_current_indent==0) return;
  1390.  
  1391.     /* we only make this one pixel high because we figure if
  1392.      * if any text gets put in it will make the object size right */
  1393.     _current_row.add_child(new spacer(_current_indent-shift,1));
  1394.   }
  1395.  
  1396.    //had:
  1397.    //* @exception general PROPAGATED
  1398.  
  1399.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1400.  
  1401.   /**
  1402.    * Insert a list item. This ends up causing whatever the current
  1403.    * object for this lists iteration to be inserted.
  1404.    * 
  1405.    */
  1406.   public void add_list_item() 
  1407.   {
  1408.     interactor marker;
  1409.     int i;
  1410.     html_element el;
  1411.     
  1412.     /* get the most current list item implementation */
  1413.     marker=_current_list_item_prefix.list_item_prefix();
  1414.  
  1415.     /* did we get out with nothing found? */
  1416.     if (marker==null) {
  1417.       System.out.println("BAD <LI> tag! No list in progress!");
  1418.       return;
  1419.     }
  1420.  
  1421.     /* the normal thing is to finish the current line and then add the
  1422.        marker */
  1423.     _current_end_of_line.end_of_line(marker.w(),false);
  1424.     _current_row.add_child(marker);
  1425.   }
  1426.  
  1427.    //had:
  1428.    //* @exception general PROPAGATED
  1429.  
  1430. /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1431.  
  1432.   /**
  1433.    * This function gets called to handle putting things it that 
  1434.    * are used with the ampersand notation. 
  1435.    * @param String s the string that was between the ampersand and the semicolon
  1436.    */
  1437.   protected void handle_amp(String tok) 
  1438.   {
  1439.     /* check for ones we know */
  1440.     if (tok.equals("quot")) {
  1441.       _current_add_word.add_word("\"");
  1442.     } else if (tok.equals("lt")) {
  1443.       _current_add_word.add_word("<");
  1444.     } else if (tok.equals("gt")) {
  1445.       _current_add_word.add_word(">");
  1446.     }
  1447.  
  1448.     /* didn't recognize it, give up */
  1449.   }
  1450.  
  1451.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1452.  
  1453.   /**
  1454.    * Handle the end of the current paragraph. This function takes 
  1455.    * the current column and shoves it into the text flow.  It is
  1456.    * most commonly called as P is encountered.
  1457.    */
  1458.   public void paragraph_end() {
  1459.     _current_flow.add_child(_current_column);
  1460.   }
  1461.  
  1462.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1463.  
  1464.   /**
  1465.    * Handle the start of a new paragraph. This default of
  1466.    * this just makes the current column be left justified.
  1467.    * It is most commonly called when a P is encountered,
  1468.    * but can be called by tags like CENTER.
  1469.    */
  1470.   public void paragraph_start() {
  1471.     /* DANGER: IF YOU MODIFY THIS don't forget to modify
  1472.        the one in init() too */
  1473.     _current_column=new column(0 /*ignored*/,0 /* ignored */,
  1474.                    _current_flow.w()-(2*_current_flow.border()),
  1475.                    10 /* ignored */, 
  1476.                    0 /* parent is doing border */,
  1477.                    _current_flow.interchild_space(),
  1478.                    false,false,false /* crucial */,
  1479.                    column.LEFT_JUSTIFIED,
  1480.                    null);
  1481.     _current_column.
  1482.       set_h_constraint(std_function.offset(LAST_CHILD.Y2(), 0));
  1483.   }
  1484. }
  1485. /*****************************************************************/
  1486. /*            SOME EXAMPLE CLASSES FOR COMMON TAGS               */
  1487. /*****************************************************************/
  1488.  
  1489. /**
  1490.  * This private class is used to keep track of the numbering
  1491.  * and generate the numbers for numbered lists.
  1492.  */
  1493. class numbered_element extends html_element {
  1494.   
  1495.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1496.  
  1497.   /**
  1498.    * This is what number we are about to output.
  1499.    */
  1500.   int count;
  1501.   
  1502.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1503.  
  1504.   /**
  1505.    * Construct a numbered element.
  1506.    */
  1507.   public numbered_element() {
  1508.     super();
  1509.     count=1;
  1510.     indent_contribution=10;
  1511.   }
  1512.   
  1513.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1514.  
  1515.   /**
  1516.    * We overrode for the purpose of informing the system that
  1517.    * we implement the LIST_ITEM_PREFIX method.
  1518.    * 
  1519.    * @return int bitmask of the functions implemented
  1520.    */
  1521.   public int functions_implemented() {
  1522.     return LIST_ITEM_PREFIX;
  1523.   }
  1524.   
  1525.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1526.  
  1527.   /**
  1528.    * Generate the next list item prefix, which is a number.
  1529.    * 
  1530.    */
  1531.   public interactor list_item_prefix() 
  1532. {
  1533.     int c=count;
  1534.     label l;
  1535.  
  1536.     /* update count */
  1537.     count++;
  1538.  
  1539.     /* make the label */
  1540.     l=new label((new Integer(c)).toString(),_current_font);
  1541.     l.set_above_spacing(0);
  1542.  
  1543.     /* XXX should be figuring out the descent size ... it appears that
  1544.      the descent size in many cases is WAY larger than it needs to be 
  1545.      so just use 3 instead XX */
  1546.     /* l.set_below_spacing(3); */
  1547.     l.set_below_spacing(manager.get_metrics(_current_font).getDescent());
  1548.     l.set_h_spacing(2);
  1549.     return l;
  1550.   }
  1551. }
  1552.  
  1553.    //had:
  1554.    //* @exception general PROPAGATED
  1555.  
  1556. /*---------------------------------------------------------------------*/
  1557.  
  1558. /**
  1559.  * This is a private class which is used to draw a bullet. This class
  1560.  * has no input behavior it just draws a little circle in the middle
  1561.  * of its area.
  1562.  */
  1563. class bullet extends base_interactor {
  1564.  
  1565.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1566.  
  1567.   /**
  1568.    * Construct one of these dots.
  1569.    * @param int w the width of its bounding rectangle
  1570.    * @param int h the height of its bounding rectangle
  1571.    */
  1572.   public bullet(int w,int h) 
  1573. {
  1574.     super(0,0,w,h);
  1575.   }
  1576.  
  1577.    //had:
  1578.    //* @exception general PROPAGATED
  1579.  
  1580.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1581.  
  1582.   /**
  1583.    * Draw the little dot.
  1584.    * @param drawable d the surface to draw on
  1585.    */
  1586.   public void draw_self_local(drawable d) {
  1587.     int x=w()/2,y=h()/2;
  1588.     Color c=d.getColor(); // stash this for a sec
  1589.  
  1590.     d.setColor(html_element._current_colors.foreground());
  1591.     d.fillArc(x,y-2,4,4,0,360);
  1592.     d.setColor(c);
  1593.   }
  1594.  
  1595.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1596. }
  1597.  
  1598. /*---------------------------------------------------------------------*/
  1599.  
  1600. /**
  1601.  * This private class is used to generate the bullets for
  1602.  * non-numbered lists.
  1603.  */
  1604. class non_numbered_element extends html_element {
  1605.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1606.  
  1607.   /**
  1608.    * Construct a numbered element.
  1609.    */
  1610.   public non_numbered_element() {
  1611.     super();
  1612.     indent_contribution=10;
  1613.   }
  1614.  
  1615.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1616.  
  1617.   /**
  1618.    * Generate the next list item prefix, which is a number.
  1619.    */
  1620.   public interactor list_item_prefix() 
  1621. {
  1622.     return new bullet(10,manager.get_metrics(_current_font).getAscent()+
  1623.               manager.get_metrics(_current_font).getDescent());
  1624.   }
  1625.  
  1626.    //had:
  1627.    //* @exception general PROPAGATED
  1628.  
  1629.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1630.  
  1631.   /**
  1632.    * We overrode for the purpose of informing the system that
  1633.    * we implement the LIST_ITEM_PREFIX method.
  1634.    * 
  1635.    * @return int bitmask of the functions implemented
  1636.    */
  1637.   public int functions_implemented() {
  1638.     return LIST_ITEM_PREFIX;
  1639.   }
  1640.  
  1641.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1642. }
  1643.   
  1644. /*---------------------------------------------------------------------*/
  1645.  
  1646. /**
  1647.  * This class is used to implement the CENTERED tag. It just changes
  1648.  * the type of column that gets used to put the rows in.
  1649.  */
  1650. class centered_element extends html_element {
  1651.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1652.  
  1653.   /**
  1654.    * Construct a centered  element.
  1655.    */
  1656.   public centered_element() {
  1657.     super();
  1658.   }
  1659.  
  1660.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1661.  
  1662.   /**
  1663.    * We overrode for the purpose of informing the system that
  1664.    * we implement the PARAGRAPH_START method.
  1665.    * 
  1666.    * @return int bitmask of the functions implemented
  1667.    */
  1668.   public int functions_implemented() {
  1669.     return (PARAGRAPH_START);
  1670.   }
  1671.  
  1672.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1673.  
  1674.   /**
  1675.    * Handle the start of a new paragraph. We want to create 
  1676.    * a centered column.
  1677.    */
  1678.   public void paragraph_start() {
  1679.     _current_column=new column(0 /*ignored*/,0 /* ignored */,
  1680.                    _current_flow.w()-(2*_current_flow.border()),
  1681.                    10 /* ignored */, 
  1682.                    0/* parent is doing border */,
  1683.                    _current_flow.interchild_space(),
  1684.                    false,false,false,
  1685.                    column.CENTER_JUSTIFIED,
  1686.                    null);
  1687.     _current_column.
  1688.       set_h_constraint(std_function.offset(LAST_CHILD.Y2(), 0)); 
  1689.   }
  1690.   /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */
  1691. }
  1692.  
  1693. /*---------------------------------------------------------------------*/
  1694. /*=========================== COPYRIGHT NOTICE ===========================
  1695.  
  1696. This file is part of the subArctic user interface toolkit.
  1697.  
  1698. Copyright (c) 1996 Scott Hudson and Ian Smith
  1699. All rights reserved.
  1700.  
  1701. The subArctic system is freely available for most uses under the terms
  1702. and conditions described in 
  1703.   http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html 
  1704. and appearing in full in the lib/interactor.java source file.
  1705.  
  1706. The current release and additional information about this software can be 
  1707. found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
  1708.  
  1709. ========================================================================*/
  1710.